﻿using System;
using System.ComponentModel.Composition;
using Contracts;
using System.Reflection;
using Microsoft.WindowsAPICodePack;
using Microsoft.WindowsAPICodePack.Sensors;
using Microsoft.WindowsAPICodePack.Dialogs;
using System.ComponentModel;
using System.Linq;
using Windows7.Location;
using System.Xml;
using System.Xml.Linq;
using System.Windows.Forms;

namespace MefTZAddin
{
    [WpfServiceMetadata("TZAddin")]
    public class TZAddinImpl : IWpfService, INotifyPropertyChanged
    {
        string _status;

        private LatLongLocationProvider gps;
        public event EventHandler StatusUpdated;
        TimezonePickerDialog tzPicker = new TimezonePickerDialog();

        TZUserControl _optionsUI = new TZUserControl();

        public TZAddinImpl()
        {
            Console.WriteLine("TZ Changer starting up!");
        }

        #region "Basic addin stuff"
        public System.Windows.Controls.UserControl OptionsUserControl
        {
            get { return _optionsUI; }
        }

        public string Name
        {
            get { return "Time zone changer"; }
        }

        public Uri AddinIconUri
        {
            get
            {
                string u = CreateResourceUri("Globe.png");

                return new Uri(u);
            }
        }

        private string CreateResourceUri(string name)
        {
            Assembly a = Assembly.GetExecutingAssembly();
            return string.Format("pack://application:,,,/{0};component/Resources/{1}", a.GetName().Name, name);
        }

        public string Description
        {
            get { return "Change the system time zone based on your location"; }
        }

        public string Author
        {
            get { return "Arian T. Kulp"; }
        }

        public Version Version
        {
            get { return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; }
        }

        public Uri AuthorUri
        {
            get { return new Uri("http://www.ariankulp.com/"); }
        }

        public Uri UpdateUri
        {
            get { return new Uri("http://www.ariankulp.com/downloads/"); }
        }

        public string Status
        {
            get { return _status; }
            set
            {
                if (value != _status)
                {
                    _status = value;

                    OnPropertyChanged("Status");

                    if (StatusUpdated != null)
                        StatusUpdated(this, null);
                }
            }
        }

        public void Initialize()
        {
            _optionsUI.DataContext = this;
        }

        #endregion

        public void Start()
        {
            _optionsUI.DataContext = this;

            if (!OperatingSystemUtil.MeetsOrExceedsWin7())
            {
                // Fatal error
                throw new UtilityRuntimeException("This addin requires Windows 7 or higher to operate");
            }
            else
            {
                gps = new LatLongLocationProvider(30000);

                gps.LocationChanged += new LocationChangedEventHandler(gps_LocationChanged);

                LocationProvider.RequestPermissions(IntPtr.Zero, true, gps);
            }

            Status = "Time zone Changer Started";
        }

        bool updating = false;
        void gps_LocationChanged(LocationProvider locationProvider, LocationReport newLocation)
        {
            if (!updating)
            {
                updating = true;
                try
                {
                    // TODO: Somehow check for larger changes... don't respond to every movement
                    LastDataReport = newLocation as LatLongLocationReport;
                    //double? utc = LookupLocation(LastDataReport.Latitude, LastDataReport.Longitude);
                    //if (utc != null)
                    //{
                        //TimeZoneInfo newTZ = FindTimezonesByBaseUtc(utc.Value);
                        TimeZoneInfo newTZ = TimeZoneLocationService.GetTimeZoneInfoByCoordinates(LastDataReport.Latitude, LastDataReport.Longitude);
                        
                        if (newTZ == null)
                            Status = "No system timezone found for this region.";
                        else
                        {
                            if (TaskDialogSupport.MessagePrompt(newTZ))
                            {
                                DynamicTimeZoneInterop.SetDynamicTimeZone(newTZ);
                                Status = "Timezone updated to " + newTZ.DisplayName;
                            }
                        }
                    //}
                    //else Status = "No information found for this location";
                }
                catch (Exception x)
                {
                    Status = "Timezone update failed. (" + x.Message + ")";
                }
                finally
                {
                    updating = false;
                }
            }
        }

        private double? LookupLocation(double latitude, double longitude)
        {
            double? utc = TimeZoneLocationService.GetBaseUtcOffsetByCoordinates(latitude, longitude);

            double tzi = DynamicTimeZoneInterop.GetDynamicTimeZone().bias / 60.0 * -1.0;

            if (utc == null)
            {
                Status = "No information found for your current location.";
                return null;
            }
            else if (utc == tzi)
            {
                Status = "Still in the same timezone...";
                return null;
            }
            else return utc;
        }

        private TimeZoneInfo FindTimezonesByBaseUtc(double baseUtcOffset)
        {
            TimeZoneInfo newTZ = null;

            // This join does not take into account matching the daylight savings offset
            // since daylight savings dates are in a sub-collection...
            TimeZoneInfo[] tz = (from ctz in TimeZoneInfo.GetSystemTimeZones()
                                 where ctz.BaseUtcOffset.TotalHours == baseUtcOffset
                                 select ctz).ToArray();

            if (tz.Length == 1)
            {
                // Set time zone (and confirm status)
                if (TaskDialogSupport.MessagePrompt(tz[0]))
                    newTZ = tz[0];
            }
            else if (tz.Length > 0)
                newTZ = tzPicker.Prompt(tz);

            return newTZ;
        }

        private LatLongLocationReport lastDataReport;
        public LatLongLocationReport LastDataReport
        {
            get { return lastDataReport; }
            set
            {
                lastDataReport = value;
                OnPropertyChanged("LastDataReport");
            }
        }

        public void Stop()
        {
            // TODO: Detach from location provider
            Status = "TimeZone changer Stopped";
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string prop)
        {
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        #endregion
    }
}
